package jesss.commands;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.transfer.MultipleFileDownload;
import com.amazonaws.services.s3.transfer.Transfer;
import com.amazonaws.services.s3.transfer.TransferManager;
import jesss.options.ExtendedGnuParser;
import jesss.options.JesssOptions;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.cli.Options;

import java.io.File;

import static java.lang.Integer.parseInt;

/**
 * User: Lee
 * Date: 8/27/13
 * Time: 10:22 PM
 */
public class DownloadDirectoryCommand<T> extends AbstractJesssCommand {

    private final static int DEFAULT_WAIT_TIME = 5;
    private JesssOptions jesssOptions = null;
    private Options options = new Options();
    private String bucketName;
    private String key;
    private String targetDirectory;
    private int progressCheckInSeconds;
    private TransferManager transferManager;

    public DownloadDirectoryCommand(JesssOptions jesssOptions) throws Exception {
        this.jesssOptions = jesssOptions;
        setOptions();
        try {
            parseArgs(jesssOptions.getArgs());
        } catch (Exception e) {
            printUsage();
            throw e;
        }
    }

    @Override
    public CommandResult<Object> executeCommand() {
        CommandResult<Object> commandResult = new CommandResult<>();

        try {
            transferManager = new TransferManager(jesssOptions.getAmazonS3());

            displayMessage("Starting directory download...");
            MultipleFileDownload multipleFileDownload = transferManager.downloadDirectory(bucketName, key, new File(targetDirectory));
            Transfer.TransferState transferState = multipleFileDownload.getState();

            while(!transferState.equals(Transfer.TransferState.Completed)) {
                if (transferState.equals(Transfer.TransferState.Canceled)) {
                    displayMessage("Transfer was cancelled.%n");
                    break;
                } else if (transferState.equals(Transfer.TransferState.Failed)) {
                    displayMessage("Transfer has failed.%n");
                    break;
                } else if (transferState.equals(Transfer.TransferState.Waiting)) {
                    displayMessage("Transfer is waiting.%n");
                } else if (transferState.equals(Transfer.TransferState.InProgress)) {
                    displayMessage(String.format(" - bytes transferred: %02d%n", multipleFileDownload.getProgress().getBytesTransferred()));
                }
                Thread.sleep(progressCheckInSeconds * 1000);
                transferState = multipleFileDownload.getState();
            }
            transferManager.shutdownNow();
            commandResult.setResultStatus(ResultStatus.Success);
            displayMessage("Done downloading directory.");
        } catch (Exception e) {
            commandResult.setResultStatus(ResultStatus.Error);
            commandResult.setStatusMessage(String.format("Unable to download S3 directory in bucket \"%s\" with key \"%s\" to target directory \"%s\".", bucketName, key, targetDirectory));
            addExceptionInfoToCommandResult(commandResult, e);
        }

        return commandResult;
    }

    private void setOptions() {
        options = new Options();
        options.addOption("bn", "bucketName", true, "Bucket name");
        options.addOption("k", "key", true, "Key");
        options.addOption("td", "targetDirectory", true, "Target directory");
        options.addOption("pc", "progressCheckInSeconds", true, "Progress check in seconds");
    }

    private void parseArgs(String[] args) throws Exception {
        if (args == null || args.length == 0) {
            printUsage();
        }

        try {
            CommandLineParser parser = new ExtendedGnuParser(true);
            CommandLine cmd = parser.parse(options, args);

            if (cmd.hasOption("bn")) {
                bucketName = cmd.getOptionValue("bn");
            } else {
                throw new MissingArgumentException("Missing bucket name parameter.");
            }

            if (cmd.hasOption("k")) {
                key = cmd.getOptionValue("k");
            } else {
                throw new MissingArgumentException("Missing key parameter.");
            }

            if (cmd.hasOption("td")) {
                targetDirectory = cmd.getOptionValue("td");
            } else {
                throw new MissingArgumentException("Missing target directory parameter.");
            }

            if (cmd.hasOption("pc")) {
                progressCheckInSeconds = parseInt(cmd.getOptionValue("pc"));
            } else {
                progressCheckInSeconds = DEFAULT_WAIT_TIME;
            }
        } catch (Exception e) {
            printUsage();
            throw e;
        }
    }

    private void printUsage() {
        System.out.println("Usage:");
        System.out.println("jesss.jesss -cn downloadDirectory -bn <source bucket> -k <source key> -td <target directory name> [-pc <number of seconds to wait between progress checks>]");
    }
}
